package com.xinli.common.utils;

import org.apache.commons.lang3.ArrayUtils;

import javax.crypto.Cipher;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;


import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;


/**
 * <p>
 * RSA公钥/私钥/签名工具包
 * </p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢，一般文件不使用它来加密而是使用对称加密，<br/>
 * </p>
 */
public class RsaUtil {
    private final static Logger log = LoggerFactory.getLogger(RsaUtil.class);

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 最大的加密明文长度
     */
    public static final int MAX_ENCRYPT_BLOCK = 245;

    /**
     * 最大的解密密文长度
     */
    public static final int MAX_DECRYPT_BLOCK = 256;


    /**
     * 用私钥做 RSA 解密
     *
     * @param data-加密数据
     * @param privateKey-私钥
     * @return 解密后的数据
     * @date 2024/9/12
     */
    public static String decryptByPrivateKey(String data, String privateKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] plain = Base64.getDecoder().decode(data);
            byte[] decryptedData = null;
            for (int i = 0; i < plain.length; i += MAX_DECRYPT_BLOCK) {
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(plain, i, i + MAX_DECRYPT_BLOCK));
                decryptedData = ArrayUtils.addAll(decryptedData, doFinal);
            }
            return new String(decryptedData);
        } catch (Exception e) {
            log.error("通过私钥解密失败", e);
            return null;
        }
    }

    /**
     * 用公钥做RSA加密
     *
     * @param data-待加密数据源
     * @param publicKey-公钥
     * @return 加密后的密串
     * @date 2024/9/12
     */
    public static String encryptByPublicKey(String data, String publicKey) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key key = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)));
            // 对数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] plain = data.getBytes("UTF-8");
            int inputLen = plain.length;
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(plain, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(plain, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            return Base64.getEncoder().encodeToString(encryptedData);
        } catch (Exception e) {
            log.error("加密失败");
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

}
